---
sidebar_position: 1
title: useHotkeys
---

import { useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";

# useHotkeys API

Function signature:

```ts
function useHotkeys<T extends Element>(
  keys: string | string[],
  callback: (event: KeyboardEvent, handler: HotkeysEvent) => void,
  options: Options = {},
  deps: any[] = []
): React.MutableRef<T | null>
```

***

## Arguments

### `keys`

```ts
keys: string | string[]
```

Set the keystrokes we want the hook to listen to. We can use single or multiple keys,
modifier combinations, arrow keys, function keys, etc.

#### Listening to all keys

```jsx
useHotkeys('*', (_, handler) => alert(handler.key))
```

#### Using modifiers

```jsx
useHotkeys('ctrl+s, shift+w', () => alert('We\'re using modifiers now!'))
```

#### Using F keys

```jsx
useHotkeys('f5', () => alert('F5 was pressed'))
```

#### Using multiple keys

```jsx
useHotkeys('w, a, s, d', () => alert('Player moved!'))
```

:::tip Differentiating between multiple possible keys

If we use a combination of possible keys that use the same hook, we can use `handler.keys` to check which key the user
pressed.

```jsx
useHotkeys('ctrl+a, shift+b, r, f', (_, handler) => {
  switch (handler.keys.join('')) {
    case 'a': alert('You pressed ctrl+a!');
      break;
    case 'b': alert('You pressed shift+b!');
      break;
    case 'r': alert('You pressed r!');
      break;
    case 'f': alert('You pressed f!');
      break;
  }
})
```

:::

This can also be an array:

```jsx
useHotkeys(['ctrl+a', 'shift+b', 'r', 'f'], (_, handler) => {
  switch (handler.keys.join('')) {
    case 'a': alert('You pressed ctrl+a!');
      break;
    case 'b': alert('You pressed shift+b!');
      break;
    case 'r': alert('You pressed r!');
      break;
    case 'f': alert('You pressed f!');
      break;
  }
})
```

Also you could work directly with hotkey trigger:

```jsx
const HOTKEYS = [
  ACTION_A: 'ctrl+a',
  ACTION_B: 'shift+b',
]

useHotkeys(HOTKEYS, (_, { hotkey }) => {
  switch (hotkey) {
    case HOTKEYS.ACTION_A: alert('You pressed ctrl+a!');
      break;
    case HOTKEYS.ACTION_B: alert('You pressed shift+b!');
      break;
  }
})
```

***

### `callback`

```ts
callback: (event: KeyboardEvent, handler: HotkeysEvent) => void
```

Gets executed when the defined keystroke gets hit by the user. `event` holds the browsers keyboard event, `handler` passes
some additional information to handle the pressed key.

#### `event`

The browsers native keyboard event that gets created when the user hits a key. For a thorough documentation of this event
check out the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent).

#### `handler`

The `handler` holds information about the pressed key. In general, we should only need this object to handle our keyboard
events.
The most important property of the `handler` object is the `keys` prop:

* `keys: string[]` - This will hold the pressed keystroke. So if we use multiple possible keystroke combinations for the same callback
we can use this property to check which specific keystroke was pressed.

> There are more properties attached to the handle that currently don't get populated with values. So we can safely ignore those.

:::info The callback gets memoised

The callback we pass into the hook gets memoised, so every variable we reference inside the callback must be added to the
dependencies array, otherwise we will get stale values. For more on memoisation in the context of React hooks read
[this nice article](https://medium.com/@sdolidze/react-hooks-memoization-99a9a91c8853).

:::

***

### `options`

We can extensively configure how the hook behaves by passing it an `options` object. Below are all properties that the
object takes.

```js
// Default values
const options = {
  enabled: true,
  enableOnFormTags: false,
  enableOnContentEditable: false,
  combinationKey: '+',
  splitKey: ',',
  scopes: '*',
  keyup: undefined,
  keydown: true,
  preventDefault: false,
  description: undefined,
  document: undefined,
  ignoreModifiers: false,
  useKey: false,
};
```

```ts
// Type Definitions
type Trigger = boolean | ((keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean)
type FormTags = 'input' | 'textarea' | 'select' | 'INPUT' | 'TEXTAREA' | 'SELECT' | 'searchbox' | 'slider' | 'spinbutton' | 'menuitem' | 'menuitemcheckbox' | 'menuitemradio' | 'option' | 'radio' | 'textbox';

type Options = {
  enabled?: Trigger
  enableOnFormTags?: FormTags[] | boolean
  enableOnContentEditable?: boolean
  combinationKey?: string
  splitKey?: string
  scopes?: string | string[]
  keyup?: boolean
  keydown?: boolean
  preventDefault?: Trigger
  description?: string
  document?: Document
  ignoreModifiers?: boolean
  useKey?: boolean
};
```

#### Properties

##### `enabled`

```ts
enabled: boolean | ((keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean) // default: true
```

Determines if the callback should get triggered. Return `false` to __prevent__ the execution
of the callback and `true` to __allow__ the callback to be triggered. You can also pass a function that returns a boolean.

##### `enableOnFormTags`

```ts
enableOnFormTags: string[] // default: undefined
```

Normally we do not want a hotkey being triggered while a user types something into an input field or a custom input that uses ARIA roles. In some cases however
this might desirable. We can enable the callback trigger for an input tag using the following values:

`INPUT`, `TEXTAREA`, `SELECT`, `ROLE_NAME_HERE`

##### `ignoreEventWhen`

```ts
ignoreEventWhen: (e: KeyboardEvent) => boolean // default: undefined
```

Provides a fine control over what events to ignore. Can be used in special cases, for example

```js
useHotkeys('a', someCallback, {
  ignoreEventWhen: (e) => {
    return e.target.className.includes('special-element')
  },
})
```

##### `enabled`

```ts
enabled: boolean // default: true
```

Setting this to `false` prevents the hook from doing anything.

##### `splitKey`

```ts
splitKey: string // default: "+"
```

Specifies the key that is used to combine multiple hotkeys into keystrokes. The default value is `+`, so `shift+a` triggers
when the user presses the "shift" key __and__ the "a" key.

##### `scopes`

To group your hotkeys into different scopes, you can pass a string or an array of strings to the `scopes` property. This
way you can easily enable or disable a group of hotkeys at once. More on this in the
[Grouping Hotkeys together](/docs/documentation/hotkeys-provider) section.
By default all hotkeys are assigned to the wildcard `*` scope.

##### `keyup`

```ts
keyup: boolean // default: false
```

Set this to `true` if we want the hook to trigger our callback on the browsers `keyUp` event.

##### `keydown`

```ts
keydown: boolean // default: true
```

Set this to `true` if we want the hook to trigger our callback on the browsers `keyDown` event. This is the default behavior.

:::tip Setting `keydown` __and__ `keyup`
If we set `keyup` to `true` and __don't__ set the `keydown` prop (leaving the default), React Hotkeys Hook will assume
that we want to only listen to the browsers `keyUp` event.

If we in fact want the callback to get triggered by both events, we have to explicitly set both properties like so:

```js
useHotkeys('a', () => someCallback, {
  keydown: true,
  keyup: true
})
```

:::

##### `preventDefault`

```ts
preventDefault: boolean | ((keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean) // default: false
```

This flag determines if the default browser behavior should be prevented. `false` is the default value, so the browser
will proceed with its default behavior. Setting this to `true` will prevent __some__ of the default browser behavior.

A good example for this behavior is the override of `meta+s`, which normally triggers a save page dialog
inside the browser.

```js
useHotkeys('meta+s', someCallback, {
  // This will prevent the browser from showing the save page dialog
  preventDefault: true,
});
```

##### `document`

if our React app uses iframes, we can pass the `document` object of the iframe to the hook. This way the hook will bind
the hotkeys to the iframe instead of the main document.

```js
import FrameComponent from 'react-frame-component'

const InsideFrameComponent = () => {
  const { document } = useFrame()

  useHotkeys("s", () => console.log("I am triggered inside an iframe"), { document })

  return <div>....</div>
}

function App() {
  return (
    <FrameComponent>
      <InsideFrameComponent/>
    </FrameComponent>
  )
}
```

##### `ignoreModifiers`

```ts
ignoreModifiers: boolean // default: false
```

When listening to keystrokes, we can ignore the modifier keys (e.g. `shift`, `alt`, `ctrl`, `meta`) by setting this option
to true. This is especially useful when we want to to listen to secondary key combinations like `shift+1` (producing
the exclamation point `!`). A common use case for this would be to listen to the `/` Character in order to focus a search
input field.

```tsx
function App() {
  useHotkeys('/', () => inputRef.current?.focus(), { ignoreModifiers: true, preventDefault: true })

  const inputRef = useRef<HTMLInputElement>(null)

  return (
    <input type='text' ref={inputRef} placeholder="search via '/'" />
  )
}
```

:::info Why is this necessary?
There are tons of keyboard layouts all across different languages and operating systems. For example, we reach the
hashtag sign `#` via `Shift+3` on a US keyboard, but on a German keyboard it has its own dedicated key. On the other hand
in a German keyboard layout we reach the character `[` via `Option+5` on macOS, but on windows there is a dedicated key for that.

This is already confusing, and it gets even worse when we consider that users can customize their keyboard layout.
So listening to something like `#` can or cannot involve a shift modifier.

But there are also two different scenarios possible from our apps view:
We could say that we want to listen to the `#` character, no matter if the user presses `Shift+3` or its own dedicated key.
Here we can ignore any potential modifiers, so we set `ignoreModifiers: true`.
But we could also say that we only want to listen to  `Shift+3`, no matter the produced character. In this case we do need to listen to the modifier.

With this option, react-hotkeys-hook supports both ways.
:::

##### `useKey`

```ts
useKey: boolean // default: false
```

This option allows us to listen to the produced key instead of the key code. This is useful if we want to listen to a
specific key (e.g. `!`, `:`, `%`, etc.) and don't care about how the user produces that key.

***

### `deps`

```ts
deps: any[] // default: []
```

The dependency array lets us use the hook just like Reacts internal `useCallback` or `useMemo` hook. This is where our
dependencies of the callback live. If for example our callback actions depend on a referentially unstable value or a
value that will change over time, we should add this value to our deps array. Check out the
[documentation part](/docs/documentation/useHotkeys/setting-callback-dependencies)
for examples.

## Return value

### `React.MutableRef<T | null>`

The `useHotkeys` hook returns a React ref. This ref by default holds the value of `null`. We can use this ref to only
trigger the hotkeys if a specific element has been focused by the user.

```jsx live
function App() {
  const [count, setCount] = useState(0);
  const ref = useHotkeys("s", () => setCount((prevCount) => prevCount + 1));

  return (
    <div>
      <div style={{ padding: "30px" }}>Count: {count}</div>
      <button style={{ padding: "30px", background: "teal" }}>
        Focusing this area won't trigger the hotkey.
      </button>
      <button style={{ padding: "30px", background: "crimson" }} ref={ref}>
        Focusing this area will trigger the hotkey.
      </button>
    </div>
  );
}
```

:::info Not every html tag is able to receive focus by default
Elements that don't provide any native interactivity like `<div>`, `<span>`, `<p>`, etc. cannot receive a focus by default.
If we want to use `<div>` tags instead of the `<button>` tags in the example above we have to provide a `tabIndex` prop
to the tag. This way the focusing will work with all tags.

```jsx live
function App() {
  const [count, setCount] = useState(0);
  const ref = useHotkeys("s", () => setCount((prevCount) => prevCount + 1));

  return (
    <div>
      <div style={{ padding: "30px" }}>Count: {count}</div>
      <div style={{ padding: "30px", background: "teal" }} tabIndex={0}>
        Focusing this area won't trigger the hotkey.
      </div>
      <div style={{ padding: "30px", background: "crimson" }} ref={ref} tabIndex={0}>
        Focusing this area will trigger the hotkey.
      </div>
    </div>
  );
}
```

:::

## Function signature overloads

There is a common case where we want to pass dependencies to the hook but no options object. Normally we would need
to write that out like this:

```js
useHotkeys('a', () => someDependency, undefined, [someDependency]);
```

To streamline this use case the hook accepts function overloads. With this
we can pass a dependency array as the third argument instead of the fourth.

```ts
function useHotkeys<T extends Element>(
  keys: string,
  callback: (event: KeyboardEvent, handler: HotkeysEvent) => void,
  deps: any[] = []
): React.MutableRef<T | null>
```

So we are able to use the hook like this:

```js
useHotkeys('a', () => someDependency, [someDependency]);
```
